

public class Matrix3 {

	public enum Projection {
		ORTHOGONAL, PERSPECTIVE
	}

	public float[] data = new float[9];

	// cstr
	public Matrix3() {
	}

	// cstr
	public Matrix3(float[] data) {
		// assign reference
		this.data = data;
	}

	// cstr
	public Matrix3(float[] r1, float[] r2, float[] r3) {
		data[0] = r1[0];
		data[1] = r1[1];
		data[2] = r1[2];
		
		data[3] = r2[0];
		data[4] = r2[1];
		data[5] = r2[2];
		
		data[6] = r3[0];
		data[7] = r3[1];
		data[8] = r3[2];
	}

	public Matrix3 add(Matrix3 m) {
		Matrix3 t = new Matrix3();
		for (int i = 0; i < data.length; ++i) {
			t.data[i] = data[i] + m.data[i];
		}
		return t;
	}

	// this*m
	public Matrix3 mult(Matrix3 m) {
		Matrix3 t = new Matrix3();
		
		int a = 0;
		for(int i=0; i<3; ++i){
			a = 3*i;
			for(int j=0; j<3; ++j){
				t.data[a+j] = data[a]*m.data[j] + data[a+1]*m.data[3+j] + data[a+2]*m.data[6+j];
			}
		}
		return t;
	}

	// assume column major vector
	public Vector3 mult(Vector3 v) {
		// post mult
		float x = data[0] * v.x + data[1] * v.y + data[2] * v.z;
		float y = data[3] * v.x + data[4] * v.y + data[5] * v.z;
		float z = data[6] * v.x + data[7] * v.y + data[8] * v.z;
		return new Vector3(x, y, z);
	}

	public void mult(float s) {
		for (int i = 0; i < data.length; ++i) {
			data[i] *= s;
		}
	}

	public void invert() {
		mult(-1);
	}

	public void transpose() {
		Matrix3 mt = new Matrix3();
		for (int i = 0; i < 3; ++i) {
			for (int j = 0; j < 3; ++j) {
				mt.data[i + j * 3] = data[3 * i + j];
			}
		}
		data = mt.data;
	}

	

	public static Matrix3 getIdentity() {
		return new Matrix3(new float[] { 
				1, 0, 0, 
				0, 1, 0, 
				0, 0, 1 
				});
	}

	public static Matrix3 getScale(Vector3 v) {
		return new Matrix3(new float[] { 
				v.x, 0, 0, 
				0, v.y, 0, 
				0, 0, v.z 
				});
	}

	// inefficient since it always assumes rot around all 3 axes
	public static Matrix3 getRotate(Vector3 v) {
		// column major
		Matrix3 mx = new Matrix3(new float[] { 
				1, 0, 0, 
				0, (float) Math.cos(v.x), (float) -Math.sin(v.x), 
				0, (float) Math.sin(v.x), (float) Math.cos(v.x) 
				});
		//mx.printMatrix("MX");

		Matrix3 my = new Matrix3(new float[] { 
				(float) Math.cos(v.y), 0, (float) Math.sin(v.y), 
				0, 1, 0, 
				(float) -Math.sin(v.y), 0, (float) Math.cos(v.y) 
				});
		//my.printMatrix("MY");

		Matrix3 mz = new Matrix3(new float[] { 
				(float) Math.cos(v.z), (float) -Math.sin(v.z), 0, 
				(float) Math.sin(v.z), (float) Math.cos(v.z), 0,
				0, 0, 1 
				});
		//mz.printMatrix("MZ");

		Matrix3 rotXYZ = new Matrix3();
		rotXYZ = mx.mult(my);
		rotXYZ = rotXYZ.mult(mz);
		return rotXYZ;
	}
	

	public void printMatrix(String name) {
		System.out.println(name + " Matrix");
		System.out.print("===========================");
		for (int i = 0; i < data.length; ++i) {
			if (i % 3 == 0) {
				System.out.println("");
			}
			System.out.print(data[i] + "   ");
		}
		System.out.println("\n===========================");
	}
	
	public void printMatrix() {
		for (int i = 0; i < data.length; ++i) {
			if (i % 3 == 0) {
				System.out.println("");
			}
			System.out.print(data[i] + "   ");
		}
	}
	
	// clone matrix
	public void setTo(Matrix3 m){
		for (int i=0; i<data.length; ++i) {
			data[i] = m.data[i];
		}
	}
	
	public Matrix3 getCopy(Matrix3 m){
		Matrix3 n = new Matrix3();
		for (int i=0; i<data.length; ++i) {
			n.data[i] = m.data[i];
		}
		return n;
	}
	
	public static Matrix3 getLookAt(Vector3 eye, Vector3 center, Vector3 up) {
		/*
		 * Originally from:
		 * http://publib.boulder.ibm.com/infocenter/pseries/v5r3
		 * /index.jsp?topic=/com.ibm.aix.opengl/doc/openglrf/gluLookAt.htm The
		 * matrix M generated by the OpenGL could be computed as follows:
		 * 
		 * eye = 3d column vector (eyeX, eyeY, eyeZ)
		 * center = 3d column vector (centerX, centerY, centerZ)
		 * up = 3d column vector (upX, upY, upZ) 
		 * Compute f = center - eye 
		 * Normalize f 
		 * Compute s = f x up
		 * Normalize s
		 * Compute u = s x f
		 * (s, 0), (u, 0), (-f, 0), (-eye,1) (all column vectors)
		 */

		// get normalized orthonormal frame (basis vectors)
		Vector3 f = new Vector3();
		f.setTo(center);
		f.sub(eye);
		f.normalize();
		
		Vector3 s = f.cross(up);
		s.normalize();
		
		Vector3 u = s.cross(f);

// GLM implementation
		// from opengl reference
		/*float[] data = { 
		s.x, u.x, -f.x,
		s.y, u.y, -f.y,
		s.z, u.z, -f.z
		};*/
		
		float[] data = { 
				s.x, u.x, -f.x,
				s.y, u.y, -f.y,
				s.z, u.z, -f.z
				};
		
		return new Matrix3(data);
	}
	

}
